package com.framework.utils.property;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.framework.utils.util.StringUtils;

/**
 * 
 * <p>Description: 监听属性文件的变化，重新加载配置项</p>
 * @since 2016年11月1日
 * @author 张飞
 * <p>Copyright:Copyright(c)2016</p>
 */
public class PropertyWatchDog extends Thread {

	private static final Logger LOGGER = LoggerFactory.getLogger(PropertyWatchDog.class);

	public final static long DEFAULT_DELAY = 60000;

	protected long delay = DEFAULT_DELAY;

	private final boolean interrupted = false;

	/**
	 * configName, fileNames，缓存监听的文件路径
	 */
	private final Map<String, List<String>> configFilePathMap = new HashMap<String, List<String>>();

	public Map<String, List<String>> getConfigFilePathMap() {
		return configFilePathMap;
	}

	public void setDelay(long delay) {
		if (delay > 0) {
			this.delay = delay;
		}
	}

	// 监听文件的属性
	private final List<FileProperty> filePropertyList = new CopyOnWriteArrayList<FileProperty>();

	public PropertyWatchDog() {
		setDaemon(true);
	}

	/**
	 * 
	 * 方法用途: 加入需要监听的文件<br>
	 * 实现步骤: <br>
	 * @param configName
	 * @param fileName
	 */
	public void addConfigFiles(String configName, String... fileNames) {
		addConfigFileWithCallBack(configName, fileNames, null);
	}

	/**
	 * 
	 * 方法用途: 加入需要监听的文件，需要回调的对象<br>
	 * 实现步骤: <br>
	 * @param configName
	 * @param fileName
	 * @param reloadObj
	 */
	public void addConfigFileWithCallBack(String configName, String fileName, ReloadAware reloadObj) {
		List<String> configFilePaths = configFilePathMap.get(configName);
		if (configFilePaths == null) {
			configFilePaths = new ArrayList<String>();
			configFilePathMap.put(configName, configFilePaths);
		}

		// 获取文件的绝对路径，避免路径中的分隔符不一致的问题
		String fullFilePath = StringUtils.replace(ConfigFileManager.CONFIG_PATH + fileName, File.separator, "/");

		// 处理文件夹
		File file = new File(fullFilePath);
		if (file.isDirectory()) {
			for (File subFile : file.listFiles()) {
				addConfigFileWithCallBack(configName, fileName + File.separator + subFile.getName(), reloadObj);
			}
		} else {
			// 避免重复监听同一个文件
			if (configFilePaths.contains(fullFilePath)) {
				return;
			}

			configFilePaths.add(fullFilePath);
			// 使用文件全路径
			FileProperty fileProperty = new FileProperty(configName, fullFilePath, reloadObj);
			filePropertyList.add(fileProperty);
			checkAndConfigure(fileProperty);
		}
	}

	/**
	 * 
	 * 方法用途: 加入需要监听的文件，需要回调的对象<br>
	 * 实现步骤: <br>
	 * @param configName
	 * @param fileNames
	 * @param reloadObj
	 */
	public void addConfigFileWithCallBack(String configName, String[] fileNames, ReloadAware reloadObj) {
		for (String fileName : fileNames) {
			addConfigFileWithCallBack(configName, fileName, reloadObj);
		}
	}

	/**
	 * 
	 * 方法用途: 检测文件是否发生修改<br>
	 * 实现步骤: <br>
	 * @param fileProperty
	 */
	private void checkAndConfigure(FileProperty fileProperty) {
		File file = fileProperty.getFile();
		if (file == null) {
			file = new File(fileProperty.getFileName());
			fileProperty.setFile(file);
		}

		// 检测文件最后的修改日期
		if (file.exists()) {
			long l = file.lastModified();
			if (l > fileProperty.getLastModif()) {
				fileProperty.setLastModif(l);
				doOnChange(fileProperty);
				fileProperty.setWarnedAlready(false);
			}
		} else {
			if (!fileProperty.isWarnedAlready()) {
				fileProperty.setWarnedAlready(true);
				LOGGER.info("配置项：" + fileProperty.getConfigName() + " 文件：" + fileProperty.getFileName() + " 不存在！");
			}
		}
	}

	/**
	 * 
	 * 方法用途:  回调通知<br>
	 * 实现步骤: <br>
	 * @param fileProperty
	 */
	private void doOnChange(FileProperty fileProperty) {
		try {
			LOGGER.debug("加载：" + fileProperty.getConfigName() + " 配置文件:" + fileProperty.getFileName());
			ConfigFileManager.reload(fileProperty.getConfigName(), fileProperty.getFileName());
			if (fileProperty.getCallBackObj() != null) {
				fileProperty.getCallBackObj().reload();
			}
		} catch (Exception e) {
			LOGGER.info("加载：" + fileProperty.getConfigName() + " 配置文件:" + fileProperty.getFileName() + "错误，"
					+ e.getMessage());
		}
	}

	@Override
	public void run() {
		while (!interrupted) {
			try {
				Thread.sleep(delay);
			} catch (InterruptedException e) {
			}
			for (FileProperty fileProperty : filePropertyList) {
				checkAndConfigure(fileProperty);
			}
		}
	}

	/**
	 * 
	 * <p>Description: 缓存文件的属性</p>
	 * @since 2016年11月1日
	 * @author lenovo
	 * <p>Copyright:Copyright(c)2016</p>
	 */
	class FileProperty {

		private final String configName;

		private final String fileName;

		private ReloadAware callBackObj;

		private File file;

		// 文件最后修改时间
		private long lastModif = 0;

		// 是否已输出Log信息
		private boolean warnedAlready = false;

		public FileProperty(String configName, String fileName) {
			super();
			this.configName = configName;
			this.fileName = fileName;
		}

		public <T extends ReloadAware> FileProperty(String configName, String fileName, T callBackObj) {
			super();
			this.configName = configName;
			this.fileName = fileName;
			this.callBackObj = callBackObj;
		}

		public File getFile() {
			return file;
		}

		public void setFile(File file) {
			this.file = file;
		}

		public long getLastModif() {
			return lastModif;
		}

		public void setLastModif(long lastModif) {
			this.lastModif = lastModif;
		}

		public String getConfigName() {
			return configName;
		}

		public String getFileName() {
			return fileName;
		}

		public ReloadAware getCallBackObj() {
			return callBackObj;
		}

		public boolean isWarnedAlready() {
			return warnedAlready;
		}

		public void setWarnedAlready(boolean warnedAlready) {
			this.warnedAlready = warnedAlready;
		}

	}
}
